package service

import (
	sqlc "bgs/db/sqlc"
	"bgs/util"
	"context"
	"database/sql"
	"encoding/json"
	"fmt"
	"time"
)

// SettleOrderProfitLogs 结算分账订单的记账
func (s *UserServiceImp) SettleOrderProfitLogs(ctx context.Context, q *sqlc.Queries, orderPtr *sqlc.Order) (err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	userID := orderPtr.SpokesmanUserID.Int64
	userAccountLocked, err := s.EnsureAccountInfo(ctx, q, true, userID)
	if err != nil {
		return
	}
	slog.Infof("1、结算分账订单的记账-确保获取锁定的用户账户记录(%d) ok", userID)

	userAccountLogs, sqlErr := q.ListUnSettledUserAccountLogsForProfitedOrderByUserIDAndOrderSerialNumberToLock(ctx, sqlc.ListUnSettledUserAccountLogsForProfitedOrderByUserIDAndOrderSerialNumberToLockParams{
		UserID:            userID,
		OrderSerialNumber: orderPtr.SerialNumber,
	})
	if sqlErr != nil {
		if sql.ErrNoRows == sqlErr {
			err = fmt.Errorf("SettleOrderProfitLogs-没有找到分账记账记录")
		} else {
			err = sqlErr
		}
		return
	}
	slog.Infof("2、获取userAccountLogs ok")

	var userAccountIDs []int64
	orderBonusFromUserAccountLogs := util.AbsFromInt64ToUint64(util.FoldLeftInt64(util.ToGenericSlice(userAccountLogs), func(sum int64, v interface{}) int64 {
		val := v.(sqlc.UserAccountLog)
		userAccountIDs = append(userAccountIDs, val.ID)
		return sum + val.Amount
	}, 0))
	orderBonusFromOrder := util.AbsFromInt64ToUint64(orderPtr.BonusAmount + orderPtr.RefundBonusAmount)

	if orderBonusFromUserAccountLogs != orderBonusFromOrder {
		err = fmt.Errorf("订单和用户记账得出的订单奖励金不一致")
		return
	}

	amountFromPendingToAvailable := int64(orderBonusFromOrder)
	_, err = s.UpdateAccountForOrderBonusFromPendingAmountToAvailableAmountWithLock(ctx, q, UpdateAccountForOrderBonusFromPendingAmountToAvailableAmountWithLockParam{
		UserAccountPtr:               &userAccountLocked,
		AmountFromPendingToAvailable: amountFromPendingToAvailable,
	})
	if err != nil {
		return
	}
	slog.Infof("2、更新用户账户PendingAmount(%d->%d),Available(%d->%d) ok",
		userAccountLocked.PendingAmount,
		userAccountLocked.PendingAmount-amountFromPendingToAvailable,
		userAccountLocked.AvailableAmount,
		userAccountLocked.AvailableAmount+amountFromPendingToAvailable,
	)

	rowCount, err := q.BatchSettleUserAccountLogs(ctx, userAccountIDs)
	if err != nil {
		return
	}
	slog.Infof("3、更新用户记账标记已结算(%d)条 ok", rowCount)

	return
}

type LoggingAccountForUserTransferExt struct {
	Applied     time.Time `json:"applied"`
	OutDetailNo string    `json:"out_detail_no"`
}

// LoggingAccountForUserTransfer 用户转账(提现)记账
func (s *UserServiceImp) LoggingAccountForUserTransfer(ctx context.Context, q *sqlc.Queries, userAccountWithdrawApplymentPtr *sqlc.UserAccountWithdrawApplyment) (err error) {
	slog := util.NewContextLogger(ctx, s.slog)
	slog.Infof("==begin 用户转账(提现)记账")

	extInfoBytes, marshalErr := json.Marshal(&LoggingAccountForUserTransferExt{
		Applied:     userAccountWithdrawApplymentPtr.Applied,
		OutDetailNo: userAccountWithdrawApplymentPtr.OutDetailNo.String,
	})
	if marshalErr != nil {
		err = marshalErr
		return
	}

	loggingParam := LoggingAccountParam{
		UserID:   userAccountWithdrawApplymentPtr.UserID,
		BrandID:  0, // 提现不涉及品牌,用0
		IsIn:     false,
		Category: sqlc.EmUserAccountLogCategoryWithdraw,
		Amount:   userAccountWithdrawApplymentPtr.Amount,
		Subject:  "提现-微信零钱",
		ExtInfo:  string(extInfoBytes),
	}

	err = s.loggingAccount(ctx, q, loggingParam)
	if err != nil {
		return err
	}
	slog.Infof("3 用户账户变动日志(明细账)用户转账(提现) ok")

	slog.Infof("==end 用户转账(提现)记账 ok")
	return
}

// BatchLoggingAccountForUserTransferByOutDetailNos 批量用户转账(提现)记账
func (s *UserServiceImp) BatchLoggingAccountForUserTransfer(ctx context.Context, q *sqlc.Queries, userAccountWithdrawApplyments []sqlc.UserAccountWithdrawApplyment) (err error) {
	slog := util.NewContextLogger(ctx, s.slog)
	slog.Infof("=begin 批量用户转账(提现)记账")
	for _, item := range userAccountWithdrawApplyments {
		err = s.LoggingAccountForUserTransfer(ctx, q, &item)
		if err != nil {
			return
		}
	}
	slog.Infof("=end 批量用户转账(提现)记账 ok")
	return
}

// LoggingAccountParam 记录用户账户变动日志参数
type LoggingAccountParam struct {
	UserID   int64
	BrandID  int64
	StoreID  int64
	IsIn     bool
	Category sqlc.EmUserAccountLogCategory
	Amount   int64
	Subject  string
	ExtInfo  string
}

// LoggingAccount 记录用户账户变动日志
func (s *UserServiceImp) loggingAccount(ctx context.Context, q *sqlc.Queries, p LoggingAccountParam) (err error) {
	slog := util.NewContextLogger(ctx, s.slog)
	_, err = q.CreateUserAccountLog(ctx, sqlc.CreateUserAccountLogParams{
		UserID:   p.UserID,
		BrandID:  sqlc.NInt64FromInt64(p.BrandID),
		StoreID:  sqlc.NInt64FromInt64(p.StoreID),
		IsIn:     p.IsIn,
		Category: string(p.Category),
		Amount:   util.Int64IfThenElse(p.IsIn, 1, -1) * p.Amount,
		Subject:  p.Subject,
		ExtInfo:  p.ExtInfo,
	})
	if err != nil {
		return
	}
	slog.Infof("创建用户账户变动日志 ok")
	return
}
